范围检查

按照默认方式,标准库的 vector 并不提供对区间范围的检查。例如,

    void f()
    {
        int i = phone_book[1001].number;    // 1001超出了区间范围
        // ...
    }

这个初始化很可能将某个随机的值赋给 i,而不是给出一个错误。这当然不是人们所希望的,所以我在下面各章里要使用 vector 的一个经过简单修订的版本,它带有区间范围检查,称为 Vec。一个 Vec 就像是一个 vector,只是当某个下标超出了区间范围时,它将抛出一个类型为 out_of_range 的异常。

实现像 Vec 这样的类型以及有效地使用异常的技术将在11.12节、8.3节和第14章讨论。但无论如何,下面的定义对于本书中的所有例子都足够:

    template<class T> class Vec : public std::vector<T>
    {
    public:
        Vec() : vector<T>() {}
        Vec(int s) : vector<T>(s) {}
        T& operator[] (int i) { return at(i); }            // 检查区间范围
        const T& operator[](int i) const { return at(i); } // 检查区间范围
    };

这里的 at() 操作是 vector 的下标操作,如果它的参数超过了该 vector 的区间范围,at() 就会抛出一个 out_of_range 类型的异常。

现在回到保存名字和电话号码的问题。我们现在用一个 Vec 来保证所有超出范围的访问都将被抓住。例如,

    Vec<Entry> phone_book(1000);
    void print_entry(int i)    // 简单地使用,完全像用vector
    {
        cout << phone_book[i].name << ' ' << phone_book[i].number << '\n';
    }

一个超范围的访问将抛出一个异常,用户可以捕捉到它。例如,

    void f()
    {
        try {
            for(int i = 0; i < 10000; i++) print_entry(i);
        }
        catch(out_of_range) {
            cout << "range error\n";
        }
    }

在企图用 i=1000 访问 phone_book[i] 时,异常将被抛出,而后被捕捉。

如果用户未捕捉这类异常,程序就将按照一种定义良好的方式终止,而不会继续下去,也不会以某种未定义的方式失败。使异常所造成的奇怪现象最小化的一种方式是让 main() 采用一个 try- 块作为体:

    int main()
    try {
        // 你的代码
    }
    catch (out_of_range) {
        cerr << "range error\n";
    }
    catch (...) {
        cerr << "unknown exception thrown\n";
    }

在这里提供了一个默认的异常处理器。这样,如果我们未能捕捉某些异常,它就会在标准的错误诊断输出流 cerr(21.2.1节)打印出一个错误信息。

🔚